import newArrayProto from "./array"
import Dep from "./dep"

export function observe(data) {
  if (typeof data !== "object" || data == null) {
    return
  }

  // 检测到 data 是否被劫持过
  if (data.__ob__ instanceof Observer) {
    return data.__ob__
  }

  return new Observer(data)
}

class Observer {
  constructor(data) {
    // 针对对象/数组给的dep，注意以前是给对象的属性加的，现在是对象本身
    this.dep = new Dep()

    // 将 Observer 实例绑定到 data 身上，用于[劫持数组]的时候使用，还可以检测到data是否被劫持过
    Object.defineProperty(data, "__ob__", {
      value: this,
      enumerable: false
    })

    if (Array.isArray(data)) {
      // 更改data的原型，可以观察到数组调用方法的变化，具体是7个方法
      data.__proto__ = newArrayProto
      this.observeArray(data)
    } else {
      this.walk(data)
    }
  }

  // 劫持对象的属性
  walk(data) {
    Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
  }

  // 劫持数组，如果数组的 item 为对象，则要劫持对象的属性
  observeArray(data) {
    data.forEach(item => observe(item))
  }
}

// 重新定义属性
export function defineReactive(target, key, value) {
  // value 可能是个对象，递归定义
  let childOb = observe(value)
  /**
   * 每个value都有自己的dep
   * 像<div>{{name}}</div>这样的数据，注意是视图上用到的才会收集，因为视图是编译成render函数来使用的！
   */
  let dep = new Dep()
  Object.defineProperty(target, key, {
    get() {
      if (Dep.target) {
        dep.depend()
        // 针对对象给的dep，注意以前是给对象的属性加的，现在是对象本身
        if (childOb) {
          childOb.dep.depend()

          // 如果是数组里面套数组，需要递归处理
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set(newValue) {
      if (newValue === value) return
      observe(newValue)
      value = newValue
      dep.notify() // 自动更新实现
    }
  })
}

/**
 * 递归处理 
 *  如：list :[[1,1,2], [[2,2,], [3,3,]]]
 *  vm.list[0].push(5)
 *  才会更新
 */
function dependArray(array) {
  for (let i = 0; i < array.length; i++) {
    const current = array[i]
    if (current.__ob__) {
      current.__ob__.dep.depend()
    }
    if (Array.isArray(current)) {
      dependArray(current)
    }
  }
}
